import { VMUtil } from "./VMUtil";
import { Subscriber } from "./Subscriber";
import { IObserve, IVMMain } from "../Interfaces/IMVVM";
import ADQ from "../Core/ADQ"; 
import { VMArray } from "./VMArray";

/**
 * 数据劫持
 */
export class Observe implements IObserve {
  //依赖订阅
  depSub: Subscriber;
  value: any;
  isComputed: boolean;
  vm: IVMMain;
  constructor(value: any, that: IVMMain, isComputed: boolean) {
    this.value = value;
    this.vm = that;
    this.depSub = new Subscriber(that);
    this.isComputed = isComputed;
    VMUtil.instance.Define(value, "__ob__", this);
    if (Array.isArray(value)) {
      VMArray.instance.setAugment(value);
    } else {
      if (this.isComputed) this.walkComputed(value);
      else this.walk(value);
    }
  }
  /**
   *  数据循环 遍历
   * @param obj 数据对象
   */
  walk(obj: object): void {
    var self = this;

    Object.keys(obj).forEach((key) => {
      Observe.defineReactive(obj, key, obj[key], self.vm);
    });
  }
  /**
   *  数据循环 遍历
   * @param obj 数据对象
   */
  walkComputed(obj: object): void {
    var self = this;
    Object.keys(obj).forEach((key) => {
      Observe.defineComputed(this.vm.$options.data, key, obj[key], self.vm);
    });
  }
  /**拦截数据 */
  observeArray(items) {
    //VMUtil.instance.observe(items);
  }

  observeArrays(obj, key) {
    Observe.defineReactive(obj, key, obj[key], this.vm);
  }

  /**
   * 数据拦截方法
   * @param value 拦截数据
   * @param root 根对象
   */
  static observe(value, root, isComputed) {
    if (!value || typeof value !== "object") {
      return;
    }
    let ob;
    if (
      VMUtil.instance.hasOwn(value, "__ob__") &&
      value.__ob__ instanceof Observe
    ) {
      ob = value.__ob__;
    } else {
      ob = new Observe(value, root, isComputed);
    }
    return ob;
  }

  /**
   * 数据定义  给数据设置 get set 属性 链式操作，及触发数据更新通知
   * @param obj 数据对象
   * @param key 数据键
   * @param val 数据值
   */
  static defineReactive(obj, key, val, root: IVMMain) {
    let sub = new Subscriber(root);
    let childOb = Observe.observe(val, root, false);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function () {
        if (root.$targetWatcher) {
          sub.depend();
          if (childOb) childOb.depSub.depend();
        }
        return val;
      },
      set: function (newVal) {
        if (val === newVal || (newVal !== newVal && val !== val)) {
          return;
        }
        val = newVal;
        // 监听子属性
        childOb = Observe.observe(newVal, root, false);
        // 通知数据变更
        sub.notify();
      },
    });
  }

  /**
   * 为目标设置数据并触发通知
   * @param target 设置数据对象
   * @param key 关键词
   * @param val 设置数据
   * @param root VM跟节点
   */
  static setData(target: any, key: any, val: any, root: IVMMain) {
    if (Array.isArray(target) && typeof key === "number") {
      target.length = Math.max(target.length, key);
      target.splice(key, 1, val);
      return val;
    }
    if (VMUtil.instance.hasOwn(target, key)) {
      target[key] = val;
      return val;
    }
    let ob = target.__ob__;
    if (!ob) {
      target[key] = val;
      return val;
    }
    this.defineReactive(ob.value, key, val, root);
    ob.depSub.notify();
    return val;
  }

  /**
   * 数据定义  给数据设置 get set 属性 链式操作，及触发数据更新通知
   * @param obj 数据对象
   * @param key 数据键
   * @param val 数据值
   */
  static defineComputed(obj: any, key: any, computedFunc: any, root: IVMMain) {
    let sub = new Subscriber(root);
    let computedEvent = ADQ.getHandler(root, computedFunc, false);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,

      get: function () {
        if (root.$targetWatcher) {
          sub.depend();
        }
        var value = computedEvent.run();
        return value;
      },
      set: function (newVal) {
        // 通知数据变更
        sub.notify();
      },
    });
  }
}
